// Copyright (C) 2009  Yann de ThÃ©
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


#include "time.h"

Time::Time() : JD(get_julian_from_sys()), dt(0.), t(0.), TimeWrap(1.) {
	struct timeval tv;
	if (!gettimeofday(&tv, NULL))
		a_prevRealTime = (double)tv.tv_sec + (double)tv.tv_usec*1e-6;
	else {
		std::cerr << "Erreur en initialisant le temps" << std::endl;
		a_prevRealTime = 0.;
	}
}

Time::~Time() {
}

se_date Time::getLNDate() const {
	se_date res;
	get_date(JD, &res);
	return res;
}

double Time::getSpiceTDB(void) const {
	return JDtoSpiceTDB(JD);
}

double Time::update(void) {
	struct timeval tv;
	if (!gettimeofday(&tv, NULL)) {
		dt = (double)tv.tv_sec + (double)tv.tv_usec*1e-6 - a_prevRealTime;
		a_prevRealTime += dt;
	}
	else {
		std::cerr << "Erreur dans le calcul de temps" << std::endl;
		dt = 0.;
	}
	dt *= TimeWrap;
	JD += dt/86400.;
	t += dt;
	return dt;
}

void Time::wait(double delay) {
	struct timeval tv;
	double tw=a_prevRealTime;
	while (tw-a_prevRealTime < delay) {
		if (!gettimeofday(&tv, NULL))
			tw = (double)tv.tv_sec + (double)tv.tv_usec*1e-6;
		else {
			std::cerr << "Erreur en prenant le temps" << std::endl;
			tw = a_prevRealTime;
		}
	}
	this->update();
}

double JDtoSpiceTDB(double JD) {
	double tdb;
	SpiceChar date[30];
	std::string Sdate = stringify(JD);
	Sdate = Sdate + " JD";
	size_t length;
	length = Sdate.copy(date, Sdate.size());
	date[length] = '\0';
	str2et_c(date, &tdb);
	return tdb;
}

double get_julian_from_sys() {
	double JD;
	struct se_date date;
	
	/* get sys date */
	get_date_from_sys (&date);
	JD = get_julian_day (&date);
	
	return JD;
}

void get_date_from_sys (struct se_date* date) {
	struct tm* gmt;
	struct timeval tv;
	struct timezone tz;
	
	/* get current time with microseconds precission*/
	gettimeofday (&tv, &tz);
	
	/* convert to UTC time representation */
	gmt = gmtime(&tv.tv_sec);
	
	/* fill in date struct */
	date->seconds = gmt->tm_sec + ((double)tv.tv_usec / 1000000);
	date->minutes = gmt->tm_min;
	date->hours = gmt->tm_hour;
	date->days = gmt->tm_mday;
	date->months = gmt->tm_mon + 1;
	date->years = gmt->tm_year + 1900;
}

std::string get_julian_day_str() {
	double JD = get_julian_from_sys();
	std::ostringstream oss;
	oss.setf(std::ios_base::fixed);
	oss << JD << " JD";
	//std::cout << "JD calcule: " << oss.str() << std::endl;
	return oss.str();
}

double get_julian_day (struct se_date* date) {
    double JD;
    double days;
    int a,b;
    struct se_date local_date;
	
	/* create local copy */
    memcpy (&local_date, date, sizeof (struct se_date));
	
    /* check for month = January or February */
    if (local_date.months < 3 ) {
        local_date.years--;
	    local_date.months += 12;
	}
	
	a = local_date.years / 100;
	
	/* check for Julian or Gregorian calendar (starts Oct 4th 1582) */
	if (local_date.years > 1582 || 
		(local_date.years == 1582 && 
		 (local_date.months > 10 || 
		  (local_date.months == 10 && local_date.days >= 4)))) {
			 /* Gregorian calendar */    
			 b = 2 - a + (a / 4);
		 } else {
			 /* Julian calendar */
			 b = 0;
		 }
	
	/* add a fraction of hours, minutes and secs to days*/
	days = local_date.days + (double)(local_date.hours / 24.0) + (double)(local_date.minutes / 1440.0) + (double)(local_date.seconds /  86400.0);
	
	/* now get the JD */
	JD = (int)(365.25 * (local_date.years + 4716)) + 
	(int)(30.6001 * (local_date.months + 1)) + days + b - 1524.5;
	
	return JD;
}

void get_date (double JD, struct se_date * date) {
	int A,a,B,C,D,E;
	double F,Z;
	
	JD += 0.5;
	Z = (int) JD;
	F = JD - Z;
	
	if (Z < 2299161)
		A = (int) Z;
	else {
		a = (int) ((Z - 1867216.25) / 36524.25);
		A = (int) (Z + 1 + a - (int)(a / 4));
	}
	
	B = A + 1524;
	C = (int) ((B - 122.1) / 365.25);
	D = (int) (365.25 * C);
	E = (int) ((B - D) / 30.6001);
	
	/* get the hms */
	date->hours = (int) (F * 24);
	F -= (double)date->hours / 24;
	date->minutes = (int) (F * 1440);
	F -= (double)date->minutes / 1440;
	date->seconds = F * 86400;
	
	/* get the day */
	date->days = B - D - (int)(30.6001 * E);
	
	/* get the month */
	if (E < 14)
		date->months = E - 1;
	else
		date->months = E - 13;
	
	/* get the year */
	if (date->months > 2)
		date->years = C - 4716;
	else
		date->years = C - 4715;
}
